package hudson.plugins.mercurial;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Proc;
import hudson.model.AbstractBuild;
import hudson.model.Action;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.ParametersAction;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.Result;
import hudson.model.Slave;
import hudson.model.StringParameterDefinition;
import hudson.model.StringParameterValue;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import hudson.scm.PollingResult;
import hudson.scm.SCM;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.jenkinsci.plugins.multiplescms.MultiSCM;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.FakeLauncher;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import static org.junit.Assert.*;
import org.junit.ClassRule;
import org.jvnet.hudson.test.BuildWatcher;
public abstract class SCMTestBase {
@Rule public JenkinsRule j = new JenkinsRule();
@Rule public MercurialRule m = new MercurialRule(j);
@Rule public TemporaryFolder tmp = new TemporaryFolder();
@ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
private File repo;
@Before public void setUp() throws Exception {
repo = tmp.getRoot();
}
protected abstract String hgInstallation();
protected void assertClone(String log, boolean cloneExpected) {
if (cloneExpected) {
assertTrue(log, log.contains(" clone --"));
} else {
assertTrue(log, log.contains(" update --"));
assertFalse(log, log.contains(" clone --"));
}
}
@Bug(13329)
@Test public void basicOps() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "a");
String log = m.buildAndCheck(p, "a");
assertClone(log, true);
m.touchAndCommit(repo, "b");
log = m.buildAndCheck(p, "b");
assertClone(log, false);
}
@Bug(15829)
@Test public void basicOpsSlave() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
p.setAssignedNode(j.createOnlineSlave());
m.hg(repo, "init");
m.touchAndCommit(repo, "a");
String log = m.buildAndCheck(p, "a");
assertClone(log, true);
m.touchAndCommit(repo, "b");
log = m.buildAndCheck(p, "b");
assertClone(log, false);
}
@Bug(4281)
@Test public void branches() throws Exception {
m.hg(repo, "init");
m.touchAndCommit(repo, "init");
m.hg(repo, "tag", "init");
m.touchAndCommit(repo, "default-1");
m.hg(repo, "update", "--clean", "init");
m.hg(repo, "branch", "b");
m.touchAndCommit(repo, "b-1");
FreeStyleProject p = j.createFreeStyleProject();
// Clone off b.
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), "b", null,
null, null, false));
m.buildAndCheck(p, "b-1");
m.hg(repo, "update", "--clean", "default");
m.touchAndCommit(repo, "default-2");
// Changes in default should be ignored.
assertFalse(m.pollSCMChanges(p).hasChanges());
m.hg(repo, "update", "--clean", "b");
m.touchAndCommit(repo, "b-2");
// But changes in b should be pulled.
assertTrue(m.pollSCMChanges(p).hasChanges());
m.buildAndCheck(p, "b-2");
// Switch to default branch with an existing workspace.
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
// Should now consider preexisting changesets in default to be poll
// triggers.
assertTrue(m.pollSCMChanges(p).hasChanges());
// Should switch working copy to default branch.
m.buildAndCheck(p, "default-2");
m.touchAndCommit(repo, "b-3");
// Changes in other branch should be ignored.
assertFalse(m.pollSCMChanges(p).hasChanges());
}
@Bug(1099)
@Test public void pollingLimitedToModules() throws Exception {
PollingResult pr;
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null,
"dir1 dir2", null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "dir1/f");
m.buildAndCheck(p, "dir1/f");
m.touchAndCommit(repo, "dir2/f");
pr = m.pollSCMChanges(p);
assertEquals(PollingResult.Change.SIGNIFICANT, pr.change);
m.buildAndCheck(p, "dir2/f");
m.touchAndCommit(repo, "dir3/f");
pr = m.pollSCMChanges(p);
assertEquals(PollingResult.Change.INSIGNIFICANT, pr.change);
// No support for partial checkouts yet, so workspace will contain
// everything.
m.buildAndCheck(p, "dir3/f");
/* superseded by JENKINS-7594:
// HUDSON-4972: do not pay attention to merges
// (reproduce using the pathological scenario, since reproducing the
// actual scenario
// where merge gives meaningless file list is not so easy)
hg(repo, "update", "0");
touchAndCommit(repo, "dir4/f");
hg(repo, "merge");
new FilePath(repo).child("dir2/f").write("stuff", "UTF-8");
hg(repo, "commit", "--message", "merged");
pr = pollSCMChanges(p);
assertEquals(PollingResult.Change.INSIGNIFICANT, pr.change);
buildAndCheck(p, "dir4/f");
*/
}
@Bug(6337)
@Test public void pollingLimitedToModules2() throws Exception {
PollingResult pr;
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, "dir1",
null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "starter");
m.pollSCMChanges(p);
m.buildAndCheck(p, "starter");
m.touchAndCommit(repo, "dir2/f");
pr = m.pollSCMChanges(p);
assertEquals(PollingResult.Change.INSIGNIFICANT, pr.change);
m.touchAndCommit(repo, "dir1/f");
pr = m.pollSCMChanges(p);
assertEquals(PollingResult.Change.SIGNIFICANT, pr.change);
m.buildAndCheck(p, "dir1/f");
}
@Bug(12361)
@Test public void pollingLimitedToModules3() throws Exception {
PollingResult pr;
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, "dir1/f",
null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "starter");
m.pollSCMChanges(p);
m.buildAndCheck(p, "starter");
m.touchAndCommit(repo, "dir1/g");
pr = m.pollSCMChanges(p);
assertEquals(PollingResult.Change.INSIGNIFICANT, pr.change);
m.touchAndCommit(repo, "dir1/f");
pr = m.pollSCMChanges(p);
assertEquals(PollingResult.Change.SIGNIFICANT, pr.change);
m.buildAndCheck(p, "dir1/f");
}
@Bug(13174)
@Test public void pollingIgnoresMetaFiles() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null, null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "f");
m.buildAndCheck(p, "f");
m.hg(repo, "tag", "mystuff");
assertEquals(PollingResult.Change.INSIGNIFICANT, m.pollSCMChanges(p).change);
}
@Bug(7594)
@Test public void pollingHonorsBranchMerges() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null, null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "starter");
m.pollSCMChanges(p);
m.buildAndCheck(p, "starter");
m.hg(repo, "branch", "b");
m.touchAndCommit(repo, "feature");
m.hg(repo, "update", "default");
m.hg(repo, "merge", "b");
m.hg(repo, "commit", "--message", "merged");
PollingResult pr = m.pollSCMChanges(p);
assertEquals(PollingResult.Change.SIGNIFICANT, pr.change);
m.buildAndCheck(p, "feature");
}
@Bug(7594)
@Test public void pollingHonorsBranchMergesWithModules() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, "mod1", null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "starter");
m.pollSCMChanges(p);
m.buildAndCheck(p, "starter");
m.hg(repo, "branch", "mod1dev");
m.touchAndCommit(repo, "mod1/feature");
m.hg(repo, "update", "default");
m.hg(repo, "merge", "mod1dev");
m.hg(repo, "commit", "--message", "merged");
PollingResult pr = m.pollSCMChanges(p);
assertEquals(PollingResult.Change.SIGNIFICANT, pr.change);
m.buildAndCheck(p, "mod1/feature");
m.hg(repo, "branch", "mod2dev");
m.touchAndCommit(repo, "mod2/feature");
m.hg(repo, "update", "default");
m.hg(repo, "merge", "mod2dev");
m.hg(repo, "commit", "--message", "merged");
pr = m.pollSCMChanges(p);
assertEquals(PollingResult.Change.INSIGNIFICANT, pr.change);
}
@Bug(4702)
@Test public void changelogLimitedToModules() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
// Control case: no modules specified.
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "dir1/f1");
p.scheduleBuild2(0).get();
m.touchAndCommit(repo, "dir2/f1");
Iterator<? extends ChangeLogSet.Entry> it = p.scheduleBuild2(0).get()
.getChangeSet().iterator();
assertTrue(it.hasNext());
ChangeLogSet.Entry entry = it.next();
assertEquals(Collections.singleton("dir2/f1"), new HashSet<String>(
entry.getAffectedPaths()));
assertFalse(it.hasNext());
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null,
"dir1 extra", null, null, false));
// dir2/f2 change should be ignored.
m.touchAndCommit(repo, "dir1/f2");
m.touchAndCommit(repo, "dir2/f2");
it = p.scheduleBuild2(0).get().getChangeSet().iterator();
assertTrue(it.hasNext());
entry = it.next();
assertEquals(Collections.singleton("dir1/f2"), new HashSet<String>(
entry.getAffectedPaths()));
assertFalse(it.hasNext());
// First commit should match (because at least one file does) but not
// second.
m.touchAndCommit(repo, "dir2/f3", "dir1/f3");
m.touchAndCommit(repo, "dir2/f4", "dir2/f5");
it = p.scheduleBuild2(0).get().getChangeSet().iterator();
assertTrue(it.hasNext());
entry = it.next();
assertEquals(new HashSet<String>(Arrays.asList("dir1/f3", "dir2/f3")),
new HashSet<String>(entry.getAffectedPaths()));
assertFalse(it.hasNext());
// Any module in the list can trigger an inclusion.
m.touchAndCommit(repo, "extra/f1");
it = p.scheduleBuild2(0).get().getChangeSet().iterator();
assertTrue(it.hasNext());
entry = it.next();
assertEquals(Collections.singleton("extra/f1"), new HashSet<String>(
entry.getAffectedPaths()));
assertFalse(it.hasNext());
}
@Bug(4271)
@Test public void parameterizedBuildsBranch() throws Exception {
m.hg(repo, "init");
m.touchAndCommit(repo, "trunk");
m.hg(repo, "update", "null");
m.hg(repo, "branch", "b");
m.touchAndCommit(repo, "variant");
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), "${BRANCH}",
null, null, null, false));
// SECURITY-170 - have to use ParametersDefinitionProperty
p.addProperty(new ParametersDefinitionProperty(new StringParameterDefinition("BRANCH", "b")));
String log = m.buildAndCheck(p, "variant", new ParametersAction(
new StringParameterValue("BRANCH", "b")));
assertTrue(log, log.contains("--rev b"));
assertFalse(log, log.contains("--rev ${BRANCH}"));
m.touchAndCommit(repo, "further-variant");
// the following assertion commented out as a part of the fix to
// HUDSON-6126
// assertTrue(pollSCMChanges(p));
m.buildAndCheck(p, "further-variant", new ParametersAction(
new StringParameterValue("BRANCH", "b")));
}
@Bug(9686)
@Test public void pollingExpandsParameterDefaults() throws Exception {
m.hg(repo, "init");
m.touchAndCommit(repo, "trunk");
m.hg(repo, "update", "null");
m.hg(repo, "branch", "b");
m.touchAndCommit(repo, "variant");
FreeStyleProject p = j.createFreeStyleProject();
p.addProperty(new ParametersDefinitionProperty(new StringParameterDefinition("branch", "default")));
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), "${branch}", null, null, null, false));
String log = m.buildAndCheck(p, "trunk", new ParametersAction(new StringParameterValue("branch", "default")));
assertTrue(log, log.contains("--rev default"));
/* TODO cannot behave sensibly when workspace contains a branch build because the *current* trunk revision will be seen as new; would need to compare to all historical build records, or keep a separate workspace per branch:
log = m.buildAndCheck(p, "variant", new ParametersAction(new StringParameterValue("branch", "b")));
assertTrue(log, log.contains("--rev b"));
*/
assertEquals(PollingResult.Change.NONE, m.pollSCMChanges(p).change);
m.hg(repo, "update", "default");
m.touchAndCommit(repo, "trunk2");
assertEquals(PollingResult.Change.SIGNIFICANT, m.pollSCMChanges(p).change);
}
@Bug(6517)
@Test public void fileListOmittedForMerges() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "f1");
p.scheduleBuild2(0).get();
m.hg(repo, "up", "null");
m.touchAndCommit(repo, "f2");
m.hg(repo, "merge");
m.hg(repo, "commit", "--message", "merge");
Iterator<? extends ChangeLogSet.Entry> it = p.scheduleBuild2(0).get()
.getChangeSet().iterator();
assertTrue(it.hasNext());
ChangeLogSet.Entry entry = it.next();
assertTrue(((MercurialChangeSet) entry).isMerge());
assertEquals(Collections.emptySet(),
new HashSet<String>(entry.getAffectedPaths()));
assertTrue(it.hasNext());
entry = it.next();
assertFalse(((MercurialChangeSet) entry).isMerge());
assertEquals(Collections.singleton("f2"),
new HashSet<String>(entry.getAffectedPaths()));
assertFalse(it.hasNext());
}
@Test public void changesMergedToRenamedModulesTriggerBuild() throws Exception {
m.hg(repo, "init");
m.touchAndCommit(repo, "alltogether/some_interface", "alltogether/some_class");
m.hg(repo, "branch", "stable");
//create a change in a lower branch, which should trigger a build later on
m.touchAndCommit(repo, "alltogether/some_class");
m.hg(repo, "up", "default");
m.hg(repo, "mv", "alltogether/some_interface", "api/some_interface");
m.hg(repo, "mv", "alltogether/some_class", "impl/some_class");
m.hg(repo, "commit", "--message", "reorganizing repository to properly split api and implementation");
String reorganizationCommit = m.getLastChangesetId(repo);
FreeStyleProject projectForImplModule = j.createFreeStyleProject();
projectForImplModule.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, "impl", null, null, false));
projectForImplModule.scheduleBuild2(0).get();
m.hg(repo, "merge", "stable");
m.hg(repo, "commit", "--message", "merge changes from stable branch");
String mergeCommit = m.getLastChangesetId(repo);
assertPollingResult(PollingResult.Change.SIGNIFICANT, reorganizationCommit, mergeCommit, m.pollSCMChanges(projectForImplModule));
}
@Bug(3602)
@Test public void subdirectoryCheckout() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
"repo", null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "f1");
m.buildAndCheck(p, "repo/f1");
m.touchAndCommit(repo, "f2");
m.buildAndCheck(p, "repo/f2");
m.touchAndCommit(repo, "f3");
Iterator<? extends ChangeLogSet.Entry> it = p.scheduleBuild2(0).get()
.getChangeSet().iterator();
assertTrue(it.hasNext());
ChangeLogSet.Entry entry = it.next();
assertEquals(Collections.singleton("f3"),
new HashSet<String>(entry.getAffectedPaths()));
assertFalse(it.hasNext());
}
@Test public void clean() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null, null, null, true));
m.hg(repo, "init");
m.touchAndCommit(repo, "f1");
m.buildAndCheck(p, "f1");
FilePath ws = p.getLastBuild().getWorkspace();
ws.child("junk1").write("junk", null);
ws.child("junk2").write("junk", null);
// from Hg 1.3 changelog: "update: don't unlink added files when -C/--clean is specified"
m.hg(new File(ws.getRemote()), "add", "junk2");
m.touchAndCommit(repo, "f2");
m.buildAndCheck(p, "f2");
Set<String> kids = new TreeSet<String>();
for (FilePath kid : ws.list()) {
kids.add(kid.getName());
}
assertEquals("[.hg, f1, f2]", kids.toString());
}
@Test public void multipleProjectsForSingleSource() throws Exception {
FreeStyleProject one = j.createFreeStyleProject();
FreeStyleProject two = j.createFreeStyleProject();
FreeStyleProject three = j.createFreeStyleProject();
FreeStyleProject four = j.createFreeStyleProject();
one.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
two.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
three.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), "b",
null, null, null, false));
four.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), "b", null,
null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "f1");
assertTrue(m.pollSCMChanges(one).hasChanges());
m.buildAndCheck(one, "f1");
assertTrue(m.pollSCMChanges(two).hasChanges());
m.hg(repo, "branch", "b");
m.touchAndCommit(repo, "b1");
assertFalse(m.pollSCMChanges(one).hasChanges());
m.buildAndCheck(three, "b1");
m.buildAndCheck(four, "b1");
m.touchAndCommit(repo, "b2");
assertTrue(m.pollSCMChanges(three).hasChanges());
m.buildAndCheck(three, "b2");
assertTrue(m.pollSCMChanges(four).hasChanges());
assertFalse(m.pollSCMChanges(one).hasChanges());
}
/**
* Control case for {@link #changelogOnClone()}.
*/
@Test public void changelogOnUpdate() throws Exception {
AbstractBuild<?, ?> b;
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "dir1/f1");
b = p.scheduleBuild2(0).get();
assertTrue(b.getChangeSet().isEmptySet());
m.touchAndCommit(repo, "dir2/f1");
b = p.scheduleBuild2(0).get();
assertChangeSetPaths(
Collections.singletonList(Collections.singleton("dir2/f1")), b);
m.touchAndCommit(repo, "dir3/f1");
b = p.scheduleBuild2(0).get();
assertChangeSetPaths(
Collections.singletonList(Collections.singleton("dir3/f1")), b);
}
/**
* The change log should be based on comparison with the previous build, not
* depending on the state of the current local clone. If a workspace is
* wiped out, or the build is run on a new slave, it should still result in
* the same change log. This test verifies that, by comparing the "normal"
* behavior with when the workspace is removed after every build.
*/
@Bug(10255)
@Test public void changelogOnClone() throws Exception {
AbstractBuild<?, ?> b;
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "dir1/f1");
b = p.scheduleBuild2(0).get();
assertTrue(b.getChangeSet().isEmptySet());
b.getWorkspace().deleteRecursive(); // Remove the workspace to force a
// re-clone
m.touchAndCommit(repo, "dir2/f1");
b = p.scheduleBuild2(0).get();
assertChangeSetPaths(
Collections.singletonList(Collections.singleton("dir2/f1")), b);
b.getWorkspace().deleteRecursive(); // Remove the workspace to force a
// re-clone
m.touchAndCommit(repo, "dir3/f1");
b = p.scheduleBuild2(0).get();
assertChangeSetPaths(
Collections.singletonList(Collections.singleton("dir3/f1")), b);
}
/**
* The change log should be based on comparison with the previous build, not
* depending on the state of the current local clone. When there are
* multiple nodes in use, it's possible that there will be a local clone
* that doesn't contain the same changesets as the one that was used for the
* previous build. Regardless, that shouldn't affect the change log. This
* test verifies that by running 3 builds, each for one commit, but
* alternating which node the build runs on.
*/
@Bug(10255)
@Test public void changelogFromPreviousBuild() throws Exception {
AbstractBuild<?, ?> b;
FreeStyleProject p = j.createFreeStyleProject();
Slave s1 = j.createOnlineSlave();
Slave s2 = j.createOnlineSlave();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
p.setAssignedNode(s1);
m.hg(repo, "init");
m.touchAndCommit(repo, "dir1/f1");
b = p.scheduleBuild2(0).get();
assertTrue(b.getChangeSet().isEmptySet());
p.setAssignedNode(s2);
m.touchAndCommit(repo, "dir2/f1");
b = p.scheduleBuild2(0).get();
// this isn't as notable, as it's also covered by testChangelogOnClone
// assertChangeSetPaths(Collections.singletonList(Collections.singleton("dir2/f1")),
// b);
p.setAssignedNode(s1);
m.touchAndCommit(repo, "dir3/f1");
b = p.scheduleBuild2(0).get();
assertChangeSetPaths(
Collections.singletonList(Collections.singleton("dir3/f1")), b);
}
@Bug(12162)
@Test public void changelogInMultiSCM() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
m.hg(repo, "init");
m.touchAndCommit(repo, "r1f1");
File repo2 = tmp.newFolder();
m.hg(repo2, "init");
m.touchAndCommit(repo2, "r2f1");
p.setScm(new MultiSCM(Arrays.<SCM>asList(
new MercurialSCM(hgInstallation(), repo.getPath(), null, null, "r1", null, false),
new MercurialSCM(hgInstallation(), repo2.getPath(), null, null, "r2", null, false))));
FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0).get());
m.touchAndCommit(repo, "r1f2");
m.touchAndCommit(repo2, "r2f2");
assertTrue(m.pollSCMChanges(p).hasChanges());
b = j.assertBuildStatusSuccess(p.scheduleBuild2(0).get());
List<Set<String>> paths = new ArrayList<Set<String>>();
// TODO "r1/r1f2" etc. would be preferable; probably requires determineChanges to prepend subdir?
paths.add(Collections.singleton("r1f2"));
paths.add(Collections.singleton("r2f2"));
assertChangeSetPaths(paths, b);
}
@Test public void polling() throws Exception {
AbstractBuild<?, ?> b;
PollingResult pr;
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null,
null, null, false));
p.setAssignedLabel(null); // Allow roaming
// No builds, no workspace, but an available remote repository
m.hg(repo, "init");
m.touchAndCommit(repo, "f1");
String cs1 = m.getLastChangesetId(repo);
pr = m.pollSCMChanges(p);
assertPollingResult(PollingResult.Change.INCOMPARABLE, null, null, pr);
// We have a workspace, and no new changes in remote repository
b = p.scheduleBuild2(0).get();
pr = m.pollSCMChanges(p);
assertPollingResult(PollingResult.Change.NONE, cs1, cs1, pr);
// We have a workspace, and new changes in the remote repository
m.touchAndCommit(repo, "f2");
String cs2 = m.getLastChangesetId(repo);
pr = m.pollSCMChanges(p);
assertPollingResult(PollingResult.Change.SIGNIFICANT, cs1, cs2, pr);
// We lost the workspace
b.getWorkspace().deleteRecursive();
pr = m.pollSCMChanges(p);
if (p.getScm().requiresWorkspaceForPolling()) {
assertPollingResult(PollingResult.Change.INCOMPARABLE, null, null, pr);
} else {
assertPollingResult(PollingResult.Change.NONE, cs2, cs2, pr);
}
b = p.scheduleBuild2(0).get();
// Multiple polls
m.touchAndCommit(repo, "f3");
m.touchAndCommit(repo, "f4");
String cs4 = m.getLastChangesetId(repo);
pr = m.pollSCMChanges(p);
assertPollingResult(PollingResult.Change.SIGNIFICANT, cs2, cs4, pr);
m.touchAndCommit(repo, "f5");
String cs5 = m.getLastChangesetId(repo);
pr = m.pollSCMChanges(p);
assertPollingResult(PollingResult.Change.SIGNIFICANT, cs4, cs5, pr);
}
@Bug(11460)
@Test public void trailingUrlWhitespace() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath() + " ", null,
null, null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "dir1/f1");
AbstractBuild<?, ?> b = p.scheduleBuild2(0).get();
assertEquals(Result.SUCCESS, b.getResult());
}
@Bug(12829)
@Test public void nonExistingBranchesDontGenerateMercurialTagActionsInTheBuild() throws Exception {
AbstractBuild<?, ?> b;
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), "non-existing-branch", null,
null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "dir1/f1");
b = p.scheduleBuild2(0).get();
for (Action action : b.getActions()) {
if (action instanceof MercurialTagAction) {
fail("There should not be any MercurialTagAction");
}
}
}
@Bug(5396)
@Test public void tags() throws Exception {
m.hg(repo, "init");
m.touchAndCommit(repo, "f1");
m.touchAndCommit(repo, "f2");
m.hg(repo, "tag", "release");
m.touchAndCommit(repo, "f3");
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), MercurialSCM.RevisionType.TAG, "release", null, null, null, false, null));
FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
FilePath ws = b.getWorkspace();
assertTrue(ws.child("f1").exists());
assertTrue(ws.child("f2").exists());
assertFalse(ws.child("f3").exists());
m.hg(repo, "tag", "--force", "release");
b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
assertTrue(JenkinsRule.getLog(b), ws.child("f3").exists());
}
@Test public void revsets() throws Exception {
m.hg(repo, "init");
m.touchAndCommit(repo, "f1");
m.touchAndCommit(repo, "f2");
m.hg(repo, "log");
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), MercurialSCM.RevisionType.REVSET, "first(parents(tip))", null, null, null, false, null));
FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
FilePath ws = b.getWorkspace();
//We expect to be on the parent of f2: only f1 is present
assertTrue(ws.child("f1").exists());
assertFalse(ws.child("f2").exists());
m.hg(repo, "update", "-r", "0");
m.touchAndCommit(repo, "f3");
m.hg(repo, "merge");
m.hg(repo, "commit", "-m", "Merged some stuff");
b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
ws = b.getWorkspace();
//We expect to be on the first parent of the merge: f3 is not present
assertTrue(ws.child("f1").exists());
assertTrue(ws.child("f2").exists());
assertFalse(ws.child("f3").exists());
m.touchAndCommit(repo, "f4");
b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
ws = b.getWorkspace();
//We expect to be on the merge itself: f4 is not present
assertTrue(ws.child("f1").exists());
assertTrue(ws.child("f2").exists());
assertTrue(ws.child("f3").exists());
assertFalse(ws.child("f4").exists());
}
private void assertChangeSetPaths(List<? extends Set<String>> expectedChangeSetPaths,
AbstractBuild<?, ?> build) throws IOException {
ChangeLogSet<? extends Entry> actualChangeLogSet = build.getChangeSet();
List<Set<String>> actualChangeSetPaths = new LinkedList<Set<String>>();
for (Entry entry : actualChangeLogSet) {
actualChangeSetPaths.add(new LinkedHashSet<String>(entry
.getAffectedPaths()));
}
assertEquals(build.getLog(99).toString(), expectedChangeSetPaths, actualChangeSetPaths);
}
private void assertPollingResult(PollingResult.Change expectedChangeDegree,
String expectedBaselineId, String expectedRemoteId,
PollingResult actualPollingResult) {
assertNotNull(actualPollingResult);
PollingResult.Change actualChangeDegree = actualPollingResult.change;
assertEquals(expectedChangeDegree, actualChangeDegree);
if (expectedBaselineId == null) {
assertNull(actualPollingResult.baseline);
} else {
MercurialTagAction actualBaseline = (MercurialTagAction) actualPollingResult.baseline;
assertEquals(expectedBaselineId, actualBaseline.id);
}
if (expectedRemoteId == null) {
assertNull(actualPollingResult.remote);
} else {
MercurialTagAction actualRemote = (MercurialTagAction) actualPollingResult.remote;
assertEquals(expectedRemoteId, actualRemote.id);
}
}
private static final class NoopFakeLauncher implements FakeLauncher {
public Proc onLaunch(Launcher.ProcStarter p) throws IOException {
return null;
}
}
@Bug(15806)
@Test public void testPullReturnCode() throws Exception {
File repoOnline = tmp.newFolder();
File repoOffline = new File(repoOnline.getPath() + "-offline");
m.hg(repoOnline, "init");
m.touchAndCommit(repoOnline, "init");
m.hg(repoOnline, "tag", "init");
FreeStyleProject p = j.createFreeStyleProject();
MercurialSCM a = new MercurialSCM(hgInstallation(), repoOnline.getPath(), null, null, null, null, false);
p.setScm(a);
// First build to get a local checkout (no update on this one)
AbstractBuild<?, ?> b = p.scheduleBuild2(0).get();
assertEquals(Result.SUCCESS, b.getResult());
// Second build fails due to offline repo
repoOnline.renameTo(repoOffline);
b = p.scheduleBuild2(0).get();
assertNotEquals(Result.SUCCESS, b.getResult());
// Third build succeeds after repo returns
repoOffline.renameTo(repoOnline);
b = p.scheduleBuild2(0).get();
assertEquals(Result.SUCCESS, b.getResult());
}
private void initRepoWithTag() throws Exception {
m.hg(repo, "init");
m.touchAndCommit(repo, "init");
m.hg(repo, "branch", "stable");
m.touchAndCommit(repo, "stable commit");
m.hg(repo, "tag", "release");
m.hg(repo, "update", "--clean", "default");
}
@Bug(10706)
@Test public void testGetBranchFromTag() throws Exception {
initRepoWithTag();
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), MercurialSCM.RevisionType.TAG, "release", null, null, null, false, null));
FreeStyleBuild b = j.buildAndAssertSuccess(p);
MercurialTagAction action = b.getAction(MercurialTagAction.class);
assertNotNull(action);
assertEquals("stable", action.getBranch());
assertEquals("stable", b.getEnvironment().get("MERCURIAL_REVISION_BRANCH"));
}
@Bug(10706)
@Test public void testGetNoBranchFromBranch() throws Exception {
initRepoWithTag();
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), MercurialSCM.RevisionType.BRANCH, "default", null, null, null, false, null));
FreeStyleBuild b = j.buildAndAssertSuccess(p);
MercurialTagAction action = b.getAction(MercurialTagAction.class);
assertNotNull(action);
assertEquals(null, action.getBranch());
assertEquals(null, b.getEnvironment().get("MERCURIAL_REVISION_BRANCH"));
}
@Issue("JENKINS-30295")
@Test public void testChangeSetApiVersion1407Methods() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation(), repo.getPath(), null, null, null, null, false));
m.hg(repo, "init");
m.touchAndCommit(repo, "f1");
p.scheduleBuild2(0).get();
m.touchAndCommit(repo, "f2");
MercurialChangeSet changeSet = (MercurialChangeSet) p.scheduleBuild2(0).get().getChangeSet().iterator().next();
String commitId = m.getLastChangesetId(repo);
long timestampInSeconds = m.getLastChangesetUnixTimestamp(repo);
assertEquals(commitId, changeSet.getCommitId());
assertEquals(timestampInSeconds * 1000, changeSet.getTimestamp());
}
/* TODO the following will pass, but canUpdate is not going to work without further changes:
public void testParameterizedBuildsSource() throws Exception {
p = createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation, "${REPO}", null, null, null, false, false));
buildAndCheck(p, "trunk", new ParametersAction(new StringParameterValue("REPO", repo.getPath())));
String hgrc = p.getSomeWorkspace().child(".hg/hgrc").readToString();
assertTrue(hgrc.contains(repo.getPath()));
}
*/
/* TODO not yet supported; not sure how to expand var in MercurialSCM.createChangeLogParser:
public void testParameterizedBuildsModules() throws Exception {
hg(repo, "init");
touchAndCommit(repo, "trunk", "dir1/f", "dir2/f");
FreeStyleProject p = createFreeStyleProject();
p.setScm(new MercurialSCM(hgInstallation, repo.getPath(), null, "${MODULES}", null, false, false));
buildAndCheck(p, "dir1/f", new ParametersAction(new StringParameterValue("MODULES", "dir2")));
hg(repo, "update", "default");
touchAndCommit(repo, "dir1/g");
assertFalse(pollSCMChanges(p));
touchAndCommit(repo, "dir2/g");
assertTrue(pollSCMChanges(p));
}
*/
}